import utils from "@/utils/tool.js"
/**
 * 请求基础类
 */
export class RequestTaskPromise extends Promise {
    config = {}

    /**
     *
     * @param {?function(resolve:Function, reject:Function)} executor
     */
    constructor(executor = undefined) {
        let task;
        super((resolve, reject) => {
            // 必须返回一个请求task
            task = executor ? executor(resolve, reject) : null;
        });
        // 请求任务
        this.task = task;
    }

    /**
     *
     * @param {function(Response|JsonResponse|DownloadResponse)} onFulfilled
     * @param {function(Error)} onRejected
     * @returns {RequestTaskPromise}
     */
    then(onFulfilled, onRejected = undefined) {
        return super.then(onFulfilled, onRejected)._merge(this);
    }

    /**
     *
     * @param {function(Error|Object|*)} onRejected
     * @returns {RequestTaskPromise}
     */
    catch(onRejected) {
        return super.catch(onRejected)._merge(this);
    }

    /**
     *
     * @param {Function} onFinally
     * @returns {RequestTaskPromise}
     */
    finally(onFinally) {
        return super.finally(onFinally)._merge(this);
    }

    /**
     * 设置请求任务
     * @param {Object} task
     * @return {RequestTaskPromise}
     */
    setTask(task) {
        this.task = task;
        return this;
    }

    /**
     *
     * @returns {*}
     */
    getTask() {
        return this.task;
    }

    /**
     * 设置请求配置项
     * @param {Object} config
     * @return {RequestTaskPromise}
     */
    setConfig(config) {
        this.config = config || {};
        return this;
    }

    /**
     * 获取请求配置项
     * @return {Object}
     */
    getConfig() {
        return this.config || {};
    }

    /**
     * @private
     * @param {RequestTaskPromise} promise
     * @returns {RequestTaskPromise}
     */
    _merge(promise) {
        // 务必要设置task和config，否则后续调用链将丢失
        return this.setTask(promise.getTask())
            .setConfig(promise.getConfig());
    }

    /**
     * 终止请求
     * @returns {RequestTaskPromise}
     */
    abort() {
        this.task && this.task.abort();
        return this;
    }
}

/**
 * 上传请求类
 */
export class UploadTaskPromise extends RequestTaskPromise {

    /**
     * 监听进度
     * @param {function({progress:Number,totalBytesSent:Number,totalBytesExpectedToSend:Number})} callback
     * @return {UploadTaskPromise}
     */
    onProgressUpdate(callback) {
        this.task.onProgressUpdate(callback);
        return this;
    }

    /**
     * 取消监听进度
     * @param {Function} callback
     * @return {UploadTaskPromise}
     */
    offProgressUpdate(callback) {
        this.task.offProgressUpdate(callback);
        return this;
    }
}

/**
 * 下载请求类
 */
export class DownloadTaskPromise extends UploadTaskPromise {
}


/**
 * 基础响应类
 */
export class Response {
    /**
     * 基础响应数据
     * @type {Object}
     */
    response = {};

    constructor(response) {
        this.response = response || {};
        // 标准服务器返回内容，一般为json
        // this.content = response.data
		this.content = response
    }

    /**
     * 设置请求
     * @param {RequestTaskPromise} request
     * @return {Response}
     */
    setRequest(request) {
        this.request = request;
        return this;
    }

    /**
     * 获取请求
     * @return {RequestTaskPromise}
     */
    getRequest() {
        return this.request;
    }

    /**
     * 获取HTTP状态码
     * @returns {Number}
     */
    getStatusCode() {
        return this.response.statusCode;
    }

    /**
     * 获取服务器响应数据
     * @returns {Object|Array|String}
     */
    getContent() {
        return this.content;
    }

    /**
     * 设置服务器响应数据
     * @param {Object|*} content
     */
    setContent(content) {
        this.content = content;
    }

    /**
     * 获取响应header
     * @returns {Object}
     */
    getHeader() {
        return this.response.header;
    }

    /**
     * 获取HTTP错误消息
     * @returns {String}
     */
    getError() {
        return this.response.message || this.response.errMsg || this.response.msg || this.response.err_msg;
    }

    /**
     * 获取请求配置项
     * @returns {Object}
     */
    getConfig() {
        return this.config || {};
    }
}

/**
 * 与服务端通信的标准JSON响应
 * 如果与后端返回的字段不一致，可重写原方法：JsonResponse.prototype.getMessage = function(){ return this.getContent().msg };
 */
export class JsonResponse extends Response {
    data = {}

    constructor(response) {
        super(response);
        this.data = (this.getContent() || {}).data || {};
    }

    /**
     * 获取服务器返回的code
     * @return {Number}
     */
    getCode() {
        return (this.getContent() || {}).code;
    }

    /**
     * 获取服务器返回的消息
     * @return {String}
     */
    getMessage() {
        return (this.getContent() || {}).message;
    }

    /**
     * 获取服务器返回的数据
     * @return {Object}
     */
    getData() {
        return this.data;
    }

    /**
     * 设置服务器数据
     * @param {Object} data
     * @return {JsonResponse}
     */
    setData(data) {
        this.data = data || {};
        return this;
    }

    /**
     * 获取服务器数据字段
     * @param {String} key 字段名
     * @param {*} defaultValue 字段不存在时的默认值
     * @return {*}
     */
    get(key, defaultValue = undefined) {
        return this.data[key] === undefined ? defaultValue : this.data[key];
    }
}

export class DownloadResponse extends Response {
    constructor(response, config = {}) {
        super(response, config);
        this.content = response.tempFilePath || null;
    }

    /**
     * 获取下载的文件路径
     * @return {String}
     */
    getFilePath() {
        return this.getContent();
    }

    /**
     * 打开文档预览
     * @returns {Promise}
     */
    openDocument() {
        return new Promise((resolve, reject) => {
            // #ifdef H5
            // H5只能使用地址跳转
            window.location.href = this.getFilePath();
            resolve(true);
            // #endif
            // #ifndef H5
            uni.openDocument({
                filePath: this.getFilePath(),
                success: resolve,
                fail: reject
            });
            // #endif
        });
    }

    /**
     * 打开图片预览
     * @returns {void}
     */
    previewImage() {
        uni.previewImage({
            urls: [
                this.getFilePath()
            ]
        });
    }
}

/**
 * 请求类
 */
export class Request {
    defaults = {
        // 接口域名
        host: "",
        // 请求方式
        method: "GET",
        // 请求数据，GET时为queryString，post时为JSON
        data: undefined,
        // 接口数据类型
        dataType: "json",
        // 超时时间
        timeout: 10000,
        // header头
        header: {},
        // 是否显示loading效果，为false时，不显示，为true时强制显示，不传时，会延迟showLoadingDelay毫秒后显示
        showLoading: undefined,
        // 延迟多长时间显示loading效果，为0时则直接显示
        showLoadingDelay: 2000,
        // 加载中文字
        loadingText: "加载中",
        // 遇到错误时是否提示
        showError: true,
        // 默认错误消息
        defaultError: "请求失败",
        // 是否对Response对象进行校验
        validate: true,
        // 是否只返回data层数据，不为true时，返回Response对象。
        brief: true,
        /**
         * 请求拦截，在请求前对请求参数处理
         * @param {Object} config
         */
        before: (config) => config,
        /**
         * 响应拦截，在返回promise前，对promise处理
         * @param {RequestTaskPromise} promise
         * @returns {Object}
         */
        after: (promise) => promise,
        /**
         * 将响应对象转换为Response对象，默认返回JsonResponse。
         * @param {Object} response
         * @returns {Response|JsonResponse|DownloadResponse}
         */
        resolver: (response) => new JsonResponse(response),
        /**
         * 返回简易数据
         * @param {Response|JsonResponse} response
         * @returns {Object|*}
         */
        briefReturn: (response) => response.getData(),
        /**
         * 请求处理器
         * @param {Object} config
         * @returns {RequestTaskPromise}
         */
        handler: (config) => new RequestTaskPromise((resolve, reject) => uni.request({
            url: config.url,
            data: config.data,
            header: config.header,
            method: config.method,
            dataType: config.dataType,
            success: resolve,
            fail: reject
        }))
    }

    /**
     *
     * @param {Object} config 默认配置
     */
    constructor(config = {}) {
        this.defaults = {...this.defaults, ...config};
    }

    /**
     *
     * @param {Object} options
     * @returns {RequestTaskPromise|Response|Object}
     * @example 直接获取数据： const data = await request({xxx});
     * @example 获取task： const task = request({xxx});
     *                     task.abort()；
     */
    request(options) {
		
        let config = {...this.defaults, ...options};
        let loadingTimer;
        let isShowLoading = false;
        let timeoutTimer;
        // 校验参数
        if (!config.url) {
            throw new Error("缺少参数：url");
        }
        if (!config.url.startsWith("http")) {
			let host = config.host
			if(!host){
				host = utils.data.get('server_url')
			}
            config.url = host + config.url;
        }

        const showLoading = () => {
            isShowLoading = true;
            uni.showLoading({title:config.loadingText});
        }

        // 设置loading效果
        if (config.showLoading) {
            showLoading();
        } else if (config.showLoading !== false) {
            loadingTimer = setTimeout(showLoading, config.showLoadingDelay || 0);
        }
        // 请求前处理
        config = config.before(config);
        // 获取请求任务
        let promise = config.handler(config).setConfig(config);
        // 设置超时终止
        if (config.timeout) {
            // 超时自动终止请求
            timeoutTimer = setTimeout(() => {
                promise.abort();
            }, config.timeout);
        }
		
        promise = promise.finally(() => {
            // 请求结束后
            clearTimeout(timeoutTimer);
            clearTimeout(loadingTimer);
            if (isShowLoading) {
                uni.hideLoading();
            }
        }).then((response) => { 
			 
            if (!response || !response.statusCode) {
                throw new Error(config.defaultError);
            }
            // 生成响应数据
            return config.resolver(response).setRequest(promise);
        });

        // 响应拦截
        promise = config.after(promise) || promise;

        return promise.then(response => {
			 
			if(!response){
				uni.showModal({
					title: '提示',
					content: config.defaultError,
					showCancel:false 
				});
				throw new Error(config.defaultError);
			}
            // 返回数据
            if (config.brief !== false) {
                // 只返回服务器数据，不返回response对象。
                return config.briefReturn(response);
            }
            return response;
        });
    }

    /**
     * 发起GET请求
     * @param {String} uri
     * @param {Object} data
     * @param {Object} options
     * @return {RequestTaskPromise|JsonResponse|Object}
     */
    get(uri, data, options = {}) {
        return this.request({
            method: "GET",
            url: uri,
            data,
            ...options
        });
    }

    /**
     * 发起POST请求
     * @param {String} uri
     * @param {Object} data
     * @param {Object} options
     * @return {RequestTaskPromise|JsonResponse|Object}
     */
    post(uri, data, options = {}) {
        return this.request({
            method: "POST",
            url: uri,
            header: {
                'Content-Type': "application/json"
            },
            data,
            ...options
        });
    }

    /**
     * 上传单个文件
     * @param {String} uri
     * @param {String} filePath
     * @param {String} name
     * @param {Object} data
     * @param {Object} options
     * @return {UploadTaskPromise|JsonResponse|Response|Object}
     */
    upload(uri, filePath, name, data = {}, options = {}) {
        return this.request({
            url: uri,
            filePath,
            name,
            formData: data,
            handler: (options) => new UploadTaskPromise((resolve, reject) => uni.uploadFile({
                url: options.url,
                header: options.header,
                name: options.name,
                filePath: options.filePath,
                formData: options.data,
                success: resolve,
                fail: reject
            })),
            ...options
        });
    }

    /**
     * 上传多个文件
     * @param {String} uri
     * @param {Array} files
     * @param {Object} data
     * @param {Object} options
     * @return {UploadTaskPromise|JsonResponse|Response|Object}
     */
    uploadMulti(uri, files, data = {}, options = {}) {
        return this.request({
            url: uri,
            files,
            formData: data,
            handler: (options) => new UploadTaskPromise((resolve, reject) => uni.uploadFile({
                url: options.url,
                header: options.header,
                files: options.files,
                formData: options.data,
                success: resolve,
                fail: reject
            })),
            ...options
        });
    }

    /**
     * 下载文件
     * @param {String} uri
     * @param {Object} options
     * @return {DownloadTaskPromise|DownloadResponse|Object}
     */
    download(uri, options = {}) {
        return this.request({
            url: uri,
            brief: false,
            handler: (options) => new DownloadTaskPromise((resolve, reject) => uni.downloadFile({
                url: options.url,
                success: resolve,
                fail: reject
            })),
            /**
             *
             * @param {DownloadResponse} response
             */
            validator: (response) => {
                if (!response.getFilePath()) {
                    throw new Error("文件下载失败");
                }
            },
            // 返回下载响应
            resolver: (response) => new DownloadResponse(response),
            /**
             *
             * @param {DownloadResponse} response
             * @returns {string|null}
             */
            briefReturn: (response) => response.getFilePath(),
            ...options,
        });
    }
}

export default new Request();

